﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using Apache.Cassandra;
using SKBKontur.Cassandra.Logging.KeyGeneration;
using SKBKontur.Cassandra.Logging.Model;
using SKBKontur.Cassandra.Proxy;
using SKBKontur.Cassandra.Proxy.Management;
using SKBKontur.Cassandra.Proxy.Model;
using SKBKontur.Logging;
using SKBKontur.Logging.Model;
using SKBKontur.Serialization;

namespace SKBKontur.Cassandra.Logging.Storage
{
    public class SimpleCassandraLoggingConfigurator : ILevelDispatchingLogEventWriterConfigurator, ICassandraLoggingConfigurator
    {
        public ConsistencyLevel WriteConsistencyLevel { get; set; }
        public ConsistencyLevel ReadConsistencyLevel { get; set; }
        public IKeyGenerator KeyGenerator { get; set; }

        private readonly IProxyFactory[] proxyFactories;
        private readonly IStoragePool storagePool;
        public IConfigurationInfo ConfObj { get; private set; }
        public string KeySpace { get; private set; }
        public string MasterCF { get; private set; }
        public IStorageFactory StorageFactory
        {
            get { return storagePool; }
        }

        public string ExceptionCF { get; private set; }
        public IStringToByteArraySerializer Serializer { get; private set; }

        public SimpleCassandraLoggingConfigurator(IConfigurationInfo ConfObj, ILevelDispatchingLogEventWriterConfigurator ErrorLog, params IProxyFactory[] proxyFactories)
        {
            this.ConfObj = ConfObj;
            this.proxyFactories = proxyFactories;
            this.MasterCF = ConfObj.MasterCf;
            this.ExceptionCF = ConfObj.ExceptionCf;
            this.KeySpace = ConfObj.Keyspace;
            StorageClusterManager =
                new StorageClusterManager(
                    new LevelDispatchingLogEventWriter(ErrorLog),
                    proxyFactories);
            storagePool = new StoragePool(StorageClusterManager);
            Serializer = new StringToByteArraySerializer();
            WriteConsistencyLevel = ConsistencyLevel.QUORUM;
            ReadConsistencyLevel = ConsistencyLevel.QUORUM;
            KeyGenerator = new TimeAndGuidKeyGenerator();
        }

        public IStorageClusterManager StorageClusterManager { get; private set; }

        #region ILevelDispatchingLogEventWriterConfigurator Members

        public ILogEventWriter this[LogEventLevel level]
        {
            get { return CreateWriter(level); }
        }

        #endregion


        private ILogEventWriter CreateWriter(LogEventLevel level)
        {
            return new CassandraLogEventWriter(ConfObj.LogParameters(level), this);
        }
        private static void SafeDropKeyspace(IConfigurator configurator, string name)
        {
            List<KsDef> temp = configurator.Keyspaces();
            if (temp.Any(ks => ks.Name == name))
                configurator.DropKeyspace(name);
        }

        private CfDef CreateColumnFamilyDefinition(string name, string columnFamilyComparatorType)
        {
            return new CfDef
            {
                Keyspace = KeySpace,
                Name = name,
                Gc_grace_seconds = 0,
                Column_type = "Standard",
                Comparator_type = columnFamilyComparatorType,
                Row_cache_size = 0,
                Key_cache_size = 200000,
                Read_repair_chance = 1
            };
        }

        private CfDef CreateColumnFamilyDefinition(string name, string columnFamilyComparatorType, List<KeyValuePair<KeyValuePair<string, string>, bool>> indexName)
        {
            var meta = indexName.Select(s => GetMeta(s.Key.Key, s.Value, s.Key.Value)).ToList();
            var returned = CreateColumnFamilyDefinition(name, columnFamilyComparatorType);
            if (indexName.Count > 0)
                returned.Column_metadata = meta;
            return returned;
        }
        private ColumnDef GetMeta(string name, bool key, string comparator)
        {
            return key ? new ColumnDef
            {
                //  Index_name = name,
                Name = Encoding.UTF8.GetBytes(name),
                Validation_class = comparator,
                Index_type = IndexType.KEYS,
            } :
                       new ColumnDef()
                       {
                           //     Index_name = name,
                           Name = Encoding.UTF8.GetBytes(name),
                           Validation_class = comparator,
                           //     Index_type = IndexType.KEYS,
                       };
        }



        public void ConfigureNewKeySpace(List<KeyValuePair<KeyValuePair<string, string>, bool>> indexStr)
        {
            using (var configurator = proxyFactories[0].CreateConfigurator())
            {
                var cfDefs = new List<CfDef>
                                         {
                                             CreateColumnFamilyDefinition(MasterCF,
                                                                          ComparatorType.BytesType),
                                             CreateColumnFamilyDefinition("Info",
                                                                          ComparatorType.UTF8Type, indexStr),
                                             CreateColumnFamilyDefinition("Warn",
                                                                          ComparatorType.UTF8Type, indexStr),
                                             CreateColumnFamilyDefinition("Debug",
                                                                          ComparatorType.UTF8Type, indexStr),
                                             CreateColumnFamilyDefinition("Error",
                                                                          ComparatorType.UTF8Type, indexStr),
                                             CreateColumnFamilyDefinition("Fatal",
                                                                          ComparatorType.UTF8Type, indexStr),
                                             CreateColumnFamilyDefinition("Unknown",
                                                                          ComparatorType.UTF8Type, indexStr),
                                             CreateColumnFamilyDefinition(ExceptionCF,
                                                                          ComparatorType.UTF8Type, indexStr),
                                         };
                var ksDef = new KsDef
                {
                    Name = KeySpace,
                    Strategy_class = "org.apache.cassandra.locator.SimpleStrategy",
                    Replication_factor = 3,
                    Cf_defs = cfDefs
                };

                //                try
                //                {
                //                    configurator.Truncate(ksDef.Name, ksDef.Cf_defs.ToArray());
                //                    Console.WriteLine("Truncate cf");
                //                }
                //                catch (Exception)
                //                {
                SafeDropKeyspace(configurator, ksDef.Name);
                configurator.CreateKeyspace(ksDef);
                Thread.Sleep(30000);
                Console.WriteLine("Drop cf");
                //                }

            }

        }

        public void Dispose()
        {
            storagePool.Dispose();
        }
    }
}